// bzoj3110
// 题意：给定 n(<=50000)个初始为空的集合，编号 1~n， 
//       现在有 m(<=50000)个指令，指令是两种类型：
//         1. 1 a b c, 表示向编号为a~b的集合里都插入一个数 c，集合可以有重复元素;
//         2. 2 l r k，问将编号l~r的集合里的数都取出来，输出第 k 大的数。
//
// 题解：可以用整体二分的思想，对于当前二分到的区间[l, r]，
//       我们通过和mid比较，如果插入指令的值比mid大，就把这段区间都加1。
//       如果是查询就查询对应区间的和，得到该区间当前比当前mid大的元素
//       个数query，如果这个区间积累的cur+query比这个区间的k大，
//       说明这个区间的k大元素应该在[mid+1, r]这段区间里，否则就在[l, mid]
//       这段区间里，而且对于以后的mid，这些query个数肯定还是比mid大，
//       所以就把它们累计到这段区间的cur里，以后就不用算了。对于插入操作，
//       也是根据mid值把它分到前后的区间里，然后进行递归分治。 
//
//       区间覆盖可以用线段树，也可以用树状数组。我们令一个D，D[i]表示
//       [i, n]这段区间增加了D[i]这么多。然后对于[l, r]区间增加d，
//       就变成了将D[l]加d，D[r+1]减d。这个很容易用树状数组维护出来，
//       然后要求[l, r]区间的和，我们可以先考虑前缀和sum[r]
//         sum[r] = sigma((r-i+1)D[i])
//                = sigma((r+1)D[i]) - sigma(i*D[i]), (1<=i<=r)
//       前一个可以直接维护，后一个也可以新弄一个树状数组维护，
//       总得来说两个树状数组可以搞定。 然后还是整体二分的trick，
//       不要每次都初始化，把每个覆盖的区间在树状数组里减掉。 
//
//       复杂度就是： T(N)=2T(N/2)+O(Nlog(N))=O(N*log(N)*log(n))
//
// run: $exec < input
// opt: 0
// flag: -g
#include <cstdio>

long long inf = (1ll) << 40;
int const maxn = 100007;
int n, m;

struct data
{
	int type; // 1 insert c into [a, b], 2 query
	long long a, b, c;
	int l, r; long long k; // kth biggest
	long long cur, one_query;
	int ans_id;
};

data da[maxn], part1[maxn], part2[maxn];
long long ans[maxn];
long long tree_delta[maxn], tree_delta_sum[maxn];

int lowbit(int x) { return x & -x; }

void bit_update(int id, long long d, long long tree[])
{
	for (; id <= n; id += lowbit(id)) tree[id] += d;
}

long long bit_sum(int id, long long tree[])
{
	int ret = 0;
	for (; id > 0; id -= lowbit(id)) ret += tree[id];
	return ret;
}

void bit_cover(int l, int r, int d)
{
	bit_update(l, d, tree_delta);
	bit_update(r + 1, -d, tree_delta);
	bit_update(l, d * l, tree_delta_sum);
	bit_update(r + 1, -d * (r + 1), tree_delta_sum);
}

long long bit_interval(long long l, long long r)
{
	long long sr = (r + 1) * bit_sum(r, tree_delta) - bit_sum(r, tree_delta_sum);
	long long sl = l * bit_sum(l - 1, tree_delta) - bit_sum(l - 1, tree_delta_sum);
	return sr - sl;
}

void divide_and_conquer(int head, int tail, long long l, long long r)
{
//	printf("%lld %lld\n", l, r);
	if (head > tail) return;
	if (l == r) {
		for (int i = head; i <= tail; i++)
			if (da[i].type == 2) ans[da[i].ans_id] = l;
		return;
	}
	long long mid = (l + r) / 2;
	if (mid <= 0 && l < 0) mid--;

	for (int i = head; i <= tail; i++)
		if (da[i].type == 1 && da[i].c > mid)
			bit_cover(da[i].a, da[i].b, 1);
		else if (da[i].type == 2)
			da[i].one_query = bit_interval(da[i].l, da[i].r);
	for (int i = head; i <= tail; i++)
		if (da[i].type == 1 && da[i].c > mid)
			bit_cover(da[i].a, da[i].b, -1);
	int tot1 = 0, tot2 = 0;
	for (int i = head; i <= tail; i++) {
		if (da[i].type == 2) {
			if (da[i].cur + da[i].one_query >= da[i].k)
				part2[++tot2] = da[i];
			else {
				da[i].cur += da[i].one_query;
				part1[++tot1] = da[i];
			}
		} else {
			if (da[i].c > mid) part2[++tot2] = da[i];
			else part1[++tot1] = da[i];
		}
	}
	for (int i = 1; i <= tot1; i++) da[head + i - 1] = part1[i];
	for (int i = 1; i <= tot2; i++) da[head + tot1 + i - 1] = part2[i];
	divide_and_conquer(head, head + tot1 - 1, l, mid);
	divide_and_conquer(head + tot1, tail, mid + 1, r);
}

int main()
{
	scanf("%d%d", &n, &m);
	int tot = 0;
	for (int i = 1, opt; i <= m; i++) {
		scanf("%d", &opt);
		da[i].type = opt;
		if (opt == 1) {
			scanf("%lld%lld%lld", &da[i].a, &da[i].b, &da[i].c);
		} else {
			scanf("%d%d%lld", &da[i].l, &da[i].r, &da[i].k);
			da[i].ans_id = tot++;
		}
	}
	divide_and_conquer(1, m, -inf, inf);
	for (int i = 0; i < tot; i++)
		printf("%lld\n", ans[i]);
}

